#include "pch.hpp"
#include "CssParser.hpp"

using namespace Engine;
using namespace Engine::Ui;




CssParser::CssParser()
	: m_mlCommentRx("/\\*\\*.*\\*\\*/")
	, m_commentRx("\\/\\/.*\\n")
{
	m_mlCommentRx.setMinimal(true);
	m_commentRx.setMinimal(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void CssParser::registerAttribute(Attributes::Type attr, const attribute_functor& functor)
{
	m_factory.registerFunctor(attr, functor);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool CssParser::parse(QString code, CssDocument& doc, std::vector<Error>& errors)
{
	errors.clear();

	//
	// Step 1: Remove comments
	//
	code.replace(m_mlCommentRx, "");
	code.replace(m_commentRx, "");

	//
	// Step 2: Syntactic error checking & parsing to in-memory representation
	//
	QList<CssParseClass> classes;
	parse(std::string(qP(code)), classes, errors);

	//
	// Step 3: Semantic error checking & structure expansion
	//
	expand(classes, doc, errors);

	return errors.size() == 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void CssParser::parse(const std::string& code, QList<CssParseClass>& classes, std::vector<Error>& errors)
{
	m_errorStream.str("");
	std::ostream::_Mysb* oldBuff = std::cerr.rdbuf();
	std::cerr.rdbuf(m_errorStream.rdbuf());
	BOOST_SCOPE_EXIT((oldBuff))
	{
		std::cerr.rdbuf(oldBuff);
	}
	BOOST_SCOPE_EXIT_END;

	std::string::const_iterator first = code.begin();
	std::string::const_iterator last  = code.end();
	bool r = phrase_parse(first, last, m_grammar, boost::spirit::ascii::space, classes);

	if(!r || first != last)
	{
		Error err;
		err.line = 1;
		err.col  = 1;

		std::string::const_iterator start = code.begin();
		while(start != first)
		{
			if(*(start++) == '\n')
				++err.line, err.col = 1;
			else
				++err.col;
		}

		std::string errStr = boostext::format("Error at %1%: %2%") % where() % m_errorStream.str();
		err.what = errStr.c_str();
		errors.push_back(err);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void CssParser::expand(const QList<CssParseClass>& classes, CssDocument& doc, std::vector<Error>& errors)
{
	foreach(const CssParseClass& c, classes)
	{
		doc.insert(c, m_factory, errors);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

std::string CssParser::where() const
{
	return boostext::format("line %1%, col %2%") % m_line % m_col;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
